import json
import time
import threading
from datetime import datetime
from pynput import mouse, keyboard
from pathlib import Path
import sys
import os

if getattr(sys, 'frozen', False): 
    root_dir = Path(sys.executable).parent 
else:
    root_dir=Path(__file__).resolve().parent.parent.parent

# region 录制
def record_events():
    """主函数：录制事件并保存为时间戳命名的JSON文件"""
    # 内部状态变量
    events = []
    is_recording = False
    start_time = None
    drag_active = False
    lock = threading.Lock()
    
    # 事件压缩/解压功能
    # -------------------------------
    def compress_events(events, time_threshold=0.1):
        compressed = []
        current = None

        for event in events:
            if event['type'] == 'key_press':
                if current is None or current['key'] != event['key']:
                    if current is not None:
                        compressed.append(current)
                    current = {
                        'type': 'key_press',
                        'key': event['key'],
                        'first_time': event['time'],
                        'last_time': event['time'],
                        'repeat_count': 1
                    }
                elif event['time'] - current['last_time'] < time_threshold:
                    current['last_time'] = event['time']
                    current['repeat_count'] += 1
                else:
                    compressed.append(current)
                    current = {
                        'type': 'key_press',
                        'key': event['key'],
                        'first_time': event['time'],
                        'last_time': event['time'],
                        'repeat_count': 1
                    }
            else:
                if current is not None:
                    compressed.append(current)
                    current = None
                compressed.append(event)

        if current is not None:
            compressed.append(current)
        return compressed

    # -------------------------------

    # 事件处理功能
    # -------------------------------
    def generate_filename():
        """生成带时间戳的文件名"""
        return datetime.now().strftime("%Y%m%d_%H%M%S.json")

    def save_events():
        """保存事件到时间戳命名的JSON文件"""
        compressed = compress_events(events)
        # 创建script_dir目录（如果不存在）
        script_dir = root_dir / 'script_dir'
        script_dir.mkdir(parents=True, exist_ok=True)

        print(script_dir)
        # 生成完整文件路径
        filename = script_dir / generate_filename()
        with open(filename, 'w') as f:
            json.dump(compressed, f, indent=2)
        return str(filename)

    # -------------------------------

    # 事件监听回调
    # -------------------------------
    def _on_move(x, y):
        if is_recording:
            event_type = 'drag' if drag_active else 'move'
            events.append({
                'type': event_type,
                'x': x, 'y': y,
                'time': time.time() - start_time
            })

    def _on_click(x, y, button, pressed):
        nonlocal drag_active
        if is_recording:
            event_type = 'mouse_press' if pressed else 'mouse_release'
            events.append({
                'type': event_type,
                'x': x, 'y': y,
                'button': str(button),
                'time': time.time() - start_time
            })
            drag_active = pressed

    def _on_scroll(x, y, dx, dy):
        if is_recording:
            events.append({
                'type': 'scroll',
                'dx': dx, 'dy': dy,
                'time': time.time() - start_time
            })

    def _on_press(key):
        if is_recording:
            try:
                key_str = key.char
            except AttributeError:
                key_str = str(key)
            events.append({
                'type': 'key_press',
                'key': key_str,
                'time': time.time() - start_time
            })

    def _on_release(key):
        nonlocal is_recording
        if is_recording:
            try:
                key_str = key.char
            except AttributeError:
                key_str = str(key)
                
            events.append({
                'type': 'key_release',
                'key': key_str,
                'time': time.time() - start_time
            })

            with lock:
                if key == keyboard.Key.esc:
                    is_recording = False
                    return False
    # -------------------------------

    # 主录制逻辑
    # -------------------------------
    def start():
        """开始录制事件"""
        nonlocal events, is_recording, start_time, drag_active
        events.clear()
        is_recording = True
        start_time = time.time()
        drag_active = False

        # 启动监听器
        mouse_listener = mouse.Listener(
            on_move=_on_move,
            on_click=_on_click,
            on_scroll=_on_scroll
        )
        keyboard_listener = keyboard.Listener(
            on_press=_on_press,
            on_release=_on_release
        )
        
        mouse_listener.start()
        keyboard_listener.start()
        
        # 等待录制结束
        while is_recording:
            time.sleep(0.1)
        
        # 停止后保存事件
        return save_events()
    
    return start

# region 执行
def execute_events(filename):
    """播放指定路径下的事件记录文件"""
    # 路径拼接与文件读取
    full_path = os.path.join(root_dir, 'script_dir', filename)
    with open(full_path, 'r') as f:
        compressed_events = json.load(f)
    
    # 事件解压缩函数
    def expand_events(compressed_events):
        expanded = []
        for event in compressed_events:
            if 'repeat_count' in event:
                total_duration = event['last_time'] - event['first_time']
                repeat_count = event['repeat_count']
                avg_interval = total_duration / (repeat_count - 1) if repeat_count > 1 else 0
                for i in range(repeat_count):
                    expanded.append({
                        'type': 'key_press',
                        'key': event['key'],
                        'time': event['first_time'] + i * avg_interval
                    })
            else:
                expanded.append(event)
        return expanded

    # 主播放逻辑
    def play_events(events):
        mouse_ctrl = mouse.Controller()
        keyboard_ctrl = keyboard.Controller()
        stop_playback = False
        
        # 输入设备映射表
        button_mapping = {
            'Button.left': mouse.Button.left,
            'Button.right': mouse.Button.right,
            'Button.middle': mouse.Button.middle
        }
        key_mapping = {
            'ctrl': keyboard.Key.ctrl,
            'alt': keyboard.Key.alt,
            'shift': keyboard.Key.shift,
            'cmd': keyboard.Key.cmd
        }

        if not events:
            return

        start_time = events[0]['time']
        current_drag = None
        pressed_keys = {}

        for event in events:
            if stop_playback:
                break

            delay = event['time'] - start_time
            time.sleep(max(0, delay))
            start_time = event['time']

            # 鼠标移动事件
            if event['type'] == 'move':
                mouse_ctrl.position = (event['x'], event['y'])
            
            # 鼠标拖拽事件
            elif event['type'] == 'drag' and current_drag:
                mouse_ctrl.position = (event['x'], event['y'])
                current_drag = (event['x'], event['y'])
            
            # 滚轮事件
            elif event['type'] == 'scroll':
                mouse_ctrl.scroll(event['dx'], event['dy'])
            
            # 鼠标按下事件
            elif event['type'] == 'mouse_press':
                mouse_ctrl.position = (event['x'], event['y'])
                if event['button'] in button_mapping:
                    mouse_ctrl.press(button_mapping[event['button']])
                    current_drag = (event['x'], event['y'])
            
            # 鼠标释放事件
            elif event['type'] == 'mouse_release' and current_drag:
                if event['button'] in button_mapping:
                    mouse_ctrl.release(button_mapping[event['button']])
                current_drag = None
            
            # 键盘按下事件
            elif event['type'] == 'key_press':
                key = event['key']
                if key.startswith('Key.'):
                    key_name = key.split('.')[1]
                    if hasattr(keyboard.Key, key_name):
                        key_obj = getattr(keyboard.Key, key_name)
                        keyboard_ctrl.press(key_obj)
                        pressed_keys[key] = key_obj
                else:
                    keyboard_ctrl.press(key)
                    pressed_keys[key] = key
            
            # 键盘释放事件
            elif event['type'] == 'key_release' and key in pressed_keys:
                keyboard_ctrl.release(pressed_keys[key])
                del pressed_keys[key]
    
    # 执行流程
    events = expand_events(compressed_events)
    play_events(events)